Application programs are often constructed by linking several separately-compiled object modules, possibly written in different languages. The linking process combines the separate modules, through relocating symbols and resolving external references, to form a single executable module.
Interpreted applications, however, do not result in object code when translated--implying that such applications cannot be linked with the external procedures they call. Thus, calls From interpreted applications to separately-compiled procedures must be realized in another way. One approach is to link the external procedures with a separately-compiled program that serves requests for calling the procedures. (See for example, APL2 General Information (GH20-9214-4), published by IBM Corporation 1987, pp. 35-39). This type of program is executed as a separate process, which is given control whenever the interpreter detects an external procedure call in the application. This approach, however, results in undesirable process invocation and communication overhead.
A naive approach to communicate the arguments and the result between an interpreter and a loaded procedure is to include selection code in the interpreter, which will branch to a different calling sequence for each possible combination of argument and result types. Taking this approach, however, results in a totally impractical size of the selection code. For example, to support procedure calls with up 8 arguments of 5 possible types, the number of calling sequences in the selection code is greater than 2,400,000. Therefore, this approach is not a satisfactory solution to the problem.
Another way to communicate tile arguments and the result between an interpreter and a loaded procedure is to implement the calling sequence in assembler language. This approach is rather efficient From an execution time viewpoint, since the interpreter and the external procedures can communicate the arguments and/or result by directly placing them onto the run-time stack. An obvious disadvantage of this approach, however, is that it is low-level and machine-dependent. Beyond the inherent intricacies of assembler programming, additional complexity arises from the different representations of data types that procedure arguments may assume, both within and across the programming languages in which external procedures are written. Also, the assembler approach essentially involves hardware considerations unique to the particular architecture involved. It also must be uniquely tailored to the particular procedure call mechanism at hand and to the structure of the procedure activation records under the operating system used.
When an interpreted application calls a separately compiled procedure, there must be a mechanism for (1) invoking the procedure and (2) communicating the arguments and/or result between the application and the procedure. A solution to the first task is provided by the load() system call, which is supported in IBM's commercially available AIX Operating System Version 3. By using load(), an interpreter can add an object module into its address space during its execution, and then reference the external procedures contained in that module. (See M. Auslander, A. Chibib, C. Hoagland, M. Kravetz, "Dynamic Linking and Loading in the AIX System", IBM RISC System/6000 Technology, publication no. SC23-2619, IBM Corporation, 1990, pp. 150-153). The load() Function can be found on other operating systems as well. (See for example, W. Wilson, R. O. Olson, "An Approach to Genuine Dynamic linking", Software-Practice and Experience, Vol. 21 (4), April 1991, pp. 375-390). However, load() is not designed to support the communication of arguments and/or result between the issuer of load() and a dynamically loaded procedure. As a result, the regular use of load() does not allow an interpreter to call separately compiled procedures that take arguments and/or return a result.